package registry

import (
	"encoding/json"
	"github.com/go-zookeeper/zk"
	"path"
	"strings"
	"time"
	"wf-deploy-agent/conf"
	"wf-deploy-agent/infra"
	"wf-deploy-agent/registry/entity"
	"wf-deploy-agent/util"
	"wf-deploy-agent/zookeeper"
)

// ZkRegistry 实现了 Registry
type ZkRegistry struct {
	Client *zookeeper.Client
}

func NewZkRegistry() *ZkRegistry {
	r := &ZkRegistry{}
	r.Client = zookeeper.New()
	return r
}

// Prepare 注册中心初始化
func (r *ZkRegistry) Prepare() {
	r.registerRoot()
	r.registerService()
	node, err := r.registerInstance()
	if err != nil {
		infra.Logger.Fatalln(err)
	}

	go r.startGuarding(node)

}

// registerRoot 注册根节点
func (r *ZkRegistry) registerRoot() {
	err := r.Register("/", nil, 0)
	if err != nil {
		infra.Logger.Fatalln(err)
	}
}

// registerService 注册服务节点
func (r *ZkRegistry) registerService() {
	err := r.Register(conf.CF.Agent.Service.Name, nil, 0)
	if err != nil {
		infra.Logger.Fatalln(err)
	}
}

// registerInstance 注册自身实例
func (r *ZkRegistry) registerInstance() (string, error) {
	key := conf.CF.Agent.Identity
	instance := registry.ServiceRegistration{
		Id:                  key,
		Name:                conf.CF.Agent.Service.Name,
		Address:             util.GetLocalIp(),
		Port:                conf.CF.Agent.Service.Port,
		SslPort:             nil,
		RegistrationTimeUTC: time.Now().UnixMilli(),
		ServiceType:         "DYNAMIC",
		Payload: map[string]any{
			//@class为了兼容spring-cloud-starter-zookeeper-discovery
			"@class": "org.springframework.cloud.zookeeper.discovery.ZookeeperInstance",
			"id":     conf.CF.Agent.Service.Name,
			"name":   conf.CF.Agent.Service.Name,
			"metadata": map[string]any{
				"instance_status": "up",
			},
		},
		UriSpec: map[string]any{
			"parts": []map[string]any{
				{
					"value":    "scheme",
					"variable": true,
				},
				{
					"value":    "://",
					"variable": false,
				},
				{
					"value":    "address",
					"variable": true,
				}, {
					"value":    ":",
					"variable": false,
				}, {
					"value":    "port",
					"variable": true,
				},
			},
		},
	}

	bytes, err := json.Marshal(&instance)
	if err != nil {
		infra.Logger.Fatalln(err)
		return "", err
	}

	node := path.Join(conf.CF.Agent.Service.Name, key)
	err = r.Register(node, bytes)
	if err != nil {
		infra.Logger.Fatalln(err)
	}

	return node, nil
}

func (r *ZkRegistry) startGuarding(node string) {
	for {
		absNodePath := r.GetAbsPath(node)
		_, _, events, err := r.Client.GetConn().ExistsW(absNodePath)
		if err != nil {
			continue
		}

		for event := range events {
			switch event.State {
			case zk.StateDisconnected:
				_, _ = r.registerInstance()
			}

			switch event.Type {
			case zk.EventNodeDeleted:
				_, _ = r.registerInstance()
			}
		}

		<-events
	}
}

func (r *ZkRegistry) GetAbsPath(node string) string {
	if node == "" {
		infra.Logger.Errorln("node path is empty")
	}

	fullPath := path.Join("/", conf.CF.Registry.Root, node)
	if strings.HasSuffix(fullPath, "/") {
		fullPath = fullPath[0 : len(fullPath)-1]
	}

	return fullPath
}

// Register 注册服务实例
func (r *ZkRegistry) Register(key string, data []byte, flags ...int32) error {
	var flag int32 = 1
	if flags != nil && len(flags) > 0 {
		flag = flags[0]
	}

	fullPath := r.GetAbsPath(key)
	_, err := r.Client.CreateZnode(fullPath, data, flag)
	return err
}

// Unregister 删除服务实例
func (r *ZkRegistry) Unregister(key string) error {
	fullPath := r.GetAbsPath(key)

	exists, err := r.Client.Exists(fullPath)
	if err != nil || !exists {
		return err
	}

	nodes, err := r.Client.GetChildren(fullPath)
	if err != nil {
		return err
	}

	for _, n := range nodes {
		child := path.Join(fullPath, n)
		_ = r.Client.DeleteZnode(child)
	}

	return r.Client.DeleteZnode(fullPath)
}
